"""
Secure Logging Module for EndoSight-UC V4.0
Prevents information leakage through logs and implements security monitoring
"""

import json
import re
import hashlib
import time
import os
from typing import Any, Dict, Optional, List
from datetime import datetime
import structlog
from pathlib import Path


class SecureLogFilter:
    """Filter to prevent sensitive information leakage in logs"""

    # Sensitive patterns to redact
    SENSITIVE_PATTERNS = [
        # API keys and secrets
        r'(api[_-]?key["\']?\s*[:=]\s*["\']?)([a-zA-Z0-9_\-]{16,})',
        r'(secret[_-]?key["\']?\s*[:=]\s*["\']?)([a-zA-Z0-9_\-]{16,})',
        r'(token["\']?\s*[:=]\s*["\']?)([a-zA-Z0-9_\-]{16,})',

        # Passwords
        r'(password["\']?\s*[:=]\s*["\']?)([^\s"\']{6,})',

        # File paths (redact sensitive parts)
        r'([A-Za-z]:[\\/][\w\-\\/.]*)([\w\-]+\.(pkl|pth|key|pem))',

        # IP addresses (partial redaction)
        r'(\b(?:\d{1,3}\.){2}\d{1,3}\.)(\d{1,3}\b)',

        # Email addresses
        r'([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})',

        # Phone numbers
        r'(\b\d{3}[-.]?)\d{3}[-.]?\d{4}\b',

        # Credit card numbers
        r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b',

        # SQL injection patterns
        r'(union|select|insert|update|delete|drop|exec|script)',

        # Path traversal patterns
        r'(\.\.[\\/]|[\\/]\.\.[\\/])',
    ]

    # Replace patterns with redacted versions
    REPLACEMENTS = {
        'api_key': r'\1***REDACTED***',
        'secret_key': r'\1***REDACTED***',
        'token': r'\1***REDACTED***',
        'password': r'\1***REDACTED***',
        'file_path': r'\1***REDACTED***',
        'ip_address': r'\1***',
        'email': r'\1***@\2',
        'phone': r'***-***-\4',
        'credit_card': r'****-****-****-****',
        'sql_injection': r'***SQL_PATTERN***',
        'path_traversal': r'***PATH_TRAVERSAL***',
    }

    def __init__(self):
        # Compile regex patterns for performance
        self.compiled_patterns = []
        for pattern in self.SENSITIVE_PATTERNS:
            try:
                self.compiled_patterns.append(re.compile(pattern, re.IGNORECASE))
            except re.error:
                continue  # Skip invalid patterns

    def redact_sensitive_data(self, log_message: str) -> str:
        """Redact sensitive information from log messages"""
        if not isinstance(log_message, str):
            return str(log_message)

        redacted = log_message

        for pattern in self.compiled_patterns:
            try:
                redacted = pattern.sub(r'\1***REDACTED***', redacted)
            except Exception:
                continue  # Skip if replacement fails

        return redacted

    def filter_log_data(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Filter sensitive data from log dictionaries"""
        if not isinstance(data, dict):
            return data

        filtered = {}
        for key, value in data.items():
            # Skip sensitive keys entirely
            if any(sensitive in key.lower() for sensitive in
                   ['password', 'secret', 'key', 'token', 'auth']):
                filtered[key] = '***REDACTED***'
                continue

            # Recursively filter nested dictionaries
            if isinstance(value, dict):
                filtered[key] = self.filter_log_data(value)
            # Redact string values
            elif isinstance(value, str):
                filtered[key] = self.redact_sensitive_data(value)
            else:
                filtered[key] = value

        return filtered


class SecurityEventLogger:
    """Specialized logger for security events"""

    def __init__(self, log_file: Optional[str] = None):
        self.log_file = log_file or "logs/security_events.jsonl"
        self.ensure_log_directory()
        self.logger = structlog.get_logger("security")

    def ensure_log_directory(self):
        """Ensure log directory exists"""
        log_path = Path(self.log_file)
        log_path.parent.mkdir(parents=True, exist_ok=True)

    def log_security_event(self,
                          event_type: str,
                          severity: str,
                          client_ip: str,
                          details: Dict[str, Any],
                          user_agent: Optional[str] = None,
                          referer: Optional[str] = None):
        """Log a security event with standardized format"""
        try:
            event = {
                'timestamp': datetime.utcnow().isoformat(),
                'event_type': event_type,
                'severity': severity.upper(),  # LOW, MEDIUM, HIGH, CRITICAL
                'client_ip': self._partial_ip_redaction(client_ip),
                'user_agent': user_agent or 'Unknown',
                'referer': referer or 'Unknown',
                'details': self._sanitize_details(details),
                'session_id': self._generate_session_id(client_ip, user_agent)
            }

            # Write to security log file
            with open(self.log_file, 'a', encoding='utf-8') as f:
                f.write(json.dumps(event, ensure_ascii=False) + '\n')

            # Also log to structlog
            log_method = getattr(self.logger, severity.lower(), self.logger.info)
            log_method("Security event recorded",
                      event_type=event_type,
                      severity=severity,
                      client_ip=event['client_ip'])

        except Exception as e:
            # Fail silently to avoid breaking the application
            print(f"Failed to log security event: {e}")

    def _partial_ip_redaction(self, ip: str) -> str:
        """Partially redact IP address for privacy"""
        try:
            parts = ip.split('.')
            if len(parts) == 4:
                return f"{parts[0]}.{parts[1]}.{parts[2]}.***"
            return ip
        except Exception:
            return "***.***.***.***"

    def _sanitize_details(self, details: Dict[str, Any]) -> Dict[str, Any]:
        """Sanitize details to prevent information leakage"""
        filter = SecureLogFilter()
        return filter.filter_log_data(details)

    def _generate_session_id(self, ip: str, user_agent: Optional[str]) -> str:
        """Generate a consistent session ID for tracking"""
        data = f"{ip}:{user_agent or 'Unknown'}"
        return hashlib.sha256(data.encode()).hexdigest()[:16]


class AnomalyDetector:
    """Detect anomalous patterns in logs for security monitoring"""

    def __init__(self):
        self.request_patterns = {}
        self.error_threshold = 10  # Errors per minute
        self.suspicious_patterns = [
            'sql injection', 'xss', 'path traversal', 'command injection',
            'buffer overflow', 'format string', 'ldap injection'
        ]

    def analyze_log_entry(self, log_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """Analyze a log entry for anomalies"""
        anomalies = []

        # Check for suspicious patterns
        message = str(log_data.get('message', '')).lower()
        for pattern in self.suspicious_patterns:
            if pattern in message:
                anomalies.append({
                    'type': 'suspicious_pattern',
                    'pattern': pattern,
                    'severity': 'HIGH'
                })

        # Check for error spikes
        if log_data.get('level') == 'ERROR':
            current_minute = int(time.time() // 60)
            self.request_patterns[current_minute] = self.request_patterns.get(current_minute, 0) + 1

            if self.request_patterns[current_minute] > self.error_threshold:
                anomalies.append({
                    'type': 'error_spike',
                    'count': self.request_patterns[current_minute],
                    'severity': 'MEDIUM'
                })

        # Clean old patterns (older than 1 hour)
        current_minute = int(time.time() // 60)
        cutoff = current_minute - 60
        self.request_patterns = {k: v for k, v in self.request_patterns.items() if k > cutoff}

        return anomalies if anomalies else None


class SecureLoggerConfig:
    """Configuration for secure logging"""

    @staticmethod
    def configure_secure_logging():
        """Configure structlog with security filters"""
        filter = SecureLogFilter()

        def add_security_filter(_, __, event_dict):
            """Add security filtering to log processors"""
            # Filter message
            if 'event' in event_dict:
                event_dict['event'] = filter.redact_sensitive_data(event_dict['event'])

            # Filter all string values
            for key, value in event_dict.items():
                if isinstance(value, str):
                    event_dict[key] = filter.redact_sensitive_data(value)
                elif isinstance(value, dict):
                    event_dict[key] = filter.filter_log_data(value)

            return event_dict

        # Configure structlog processors
        processors = [
            structlog.stdlib.filter_by_level,
            structlog.stdlib.add_logger_name,
            structlog.stdlib.add_log_level,
            structlog.stdlib.PositionalArgumentsFormatter(),
            add_security_filter,  # Add security filtering
            structlog.processors.TimeStamper(fmt="iso"),
            structlog.processors.StackInfoRenderer(),
            structlog.processors.format_exc_info,
            structlog.processors.UnicodeDecoder(),
            structlog.processors.JSONRenderer()
        ]

        structlog.configure(
            processors=processors,
            context_class=dict,
            logger_factory=structlog.stdlib.LoggerFactory(),
            wrapper_class=structlog.stdlib.BoundLogger,
            cache_logger_on_first_use=True,
        )


class LogRetentionManager:
    """Manage log retention and cleanup"""

    def __init__(self, log_directory: str = "logs"):
        self.log_directory = Path(log_directory)
        self.retention_days = 30
        self.max_file_size_mb = 100

    def cleanup_old_logs(self):
        """Clean up old log files"""
        try:
            if not self.log_directory.exists():
                return

            cutoff_time = time.time() - (self.retention_days * 24 * 60 * 60)

            for log_file in self.log_directory.rglob("*.log*"):
                try:
                    # Check file age
                    if log_file.stat().st_mtime < cutoff_time:
                        log_file.unlink()
                        print(f"Deleted old log file: {log_file}")

                    # Check file size
                    elif log_file.stat().st_size > (self.max_file_size_mb * 1024 * 1024):
                        # Rotate large files
                        self._rotate_log_file(log_file)

                except Exception as e:
                    print(f"Failed to process log file {log_file}: {e}")

        except Exception as e:
            print(f"Log cleanup failed: {e}")

    def _rotate_log_file(self, log_file: Path):
        """Rotate a log file by renaming and creating a new one"""
        try:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            rotated_name = f"{log_file.stem}_{timestamp}{log_file.suffix}"
            rotated_path = log_file.parent / rotated_name

            log_file.rename(rotated_path)
            print(f"Rotated log file: {log_file} -> {rotated_path}")

        except Exception as e:
            print(f"Failed to rotate log file {log_file}: {e}")


# Global instances
security_logger = SecurityEventLogger()
anomaly_detector = AnomalyDetector()
log_manager = LogRetentionManager()


def log_security_event(event_type: str,
                      severity: str,
                      client_ip: str,
                      details: Dict[str, Any],
                      **kwargs):
    """Convenience function to log security events"""
    security_logger.log_security_event(event_type, severity, client_ip, details, **kwargs)


def detect_anomalies(log_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
    """Convenience function to detect log anomalies"""
    return anomaly_detector.analyze_log_entry(log_data)


# Export main functions and classes
__all__ = [
    'SecureLogFilter',
    'SecurityEventLogger',
    'AnomalyDetector',
    'SecureLoggerConfig',
    'LogRetentionManager',
    'security_logger',
    'anomaly_detector',
    'log_manager',
    'log_security_event',
    'detect_anomalies'
]